package com.miniotest.utils;

import com.miniotest.constants.HttpStatus;
import com.miniotest.config.MinioConfig;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.time.ZonedDateTime;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Component
@Slf4j
public class MinioUtils {
    @Autowired
    private MinioClient minioClient;
    @Autowired
    private MinioConfig minioConfig;

    /**
     * 创建bucket
     * @param bucketName
     * @return
     */
    public Boolean makeBucket(String bucketName) {
        try {
            if(!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())){
                minioClient.makeBucket(MakeBucketArgs.builder()
                        .bucket(bucketName)
                        .build());
            }
        } catch (Exception e) {
            log.error("minio: 创建bucket失败",e);
            return false;
        }
        return true;
    }

    /**
     * 移除bucket
     * @param bucketName
     * @return
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
            log.info("minio: 删除bucket成功",bucketName);
        } catch (Exception e) {
            log.error("minio: 移除bucket失败",e);
            return false;
        }
        return true;
    }
    public Boolean existFile(String md5){
        Iterable<Result<Item>> objectList = minioClient.listObjects(ListObjectsArgs.builder()
                .bucket(minioConfig.getBucketName())
                .build());
        try {
            for (Result<Item> itemResult : objectList) {
                if (itemResult.get().objectName().equals(md5)) {
                    return true;
                }
            }
        } catch (Exception e) {
                log.error("minio: 判断文件是否存在失败");
            }
        return false;
    }
    /**
     * 上传文件
     * @param file
     * @param fileName
     */
    public Boolean upload(File file, String fileName) {
        // 使用putObject上传一个文件到存储桶中。
        try(InputStream ips = new FileInputStream(file)) {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minioConfig.getBucketName())
                    .object(fileName)
                    .stream(ips, ips.available(), -1)
                   // .contentType(file.getContentType())
                    .build());
            return true;
        } catch (Exception e) {
            log.error("minio: 上传文件至minio失败",e);
        }
        return false;
    }

    /**
     * 通过文件输入流上传文件至minio
     * @param ips
     * @param fileName
     * @return
     */
    public Boolean uploadByIps(InputStream ips,String fileName){
        try {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minioConfig.getBucketName())
                    .object(fileName)
                    .stream(ips, ips.available(), -1)
                    // .contentType(file.getContentType())
                    .build());
            return true;
        } catch (Exception e) {
            log.error("minio: 上传文件至minio失败",e);
        }
        return false;
    }

    /**
     * 判断分片上传是否完成
     * @param md5
     * @param segNum
     * @return
     */
    public Boolean checkUploadComplete(String md5,Integer segNum){
        // bucket中分片文件名为：md5-分片号
        Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
                .bucket(minioConfig.getBucketName())
                .prefix(md5.concat("-"))
                .build());
        //将得到的所有文件片存入集合中去重
        Set<String> set = new HashSet<>();
        try {
            for (Result<Item> result : results) {
                set.add(result.get().objectName());
            }
        } catch (Exception e) {
            log.error("minio: 判断分片上传是否完成失败", e);
        }
        return set.size()==segNum;
    }

    /**
     * 分片文件合并
     * @param md5
     * @param segNum
     * @param fileName
     * @return
     */
    public Boolean mergeFile(String md5,Integer segNum,String fileName){
        // 将分片文件从tempBucket中取出
        List<ComposeSource> composeSourceList = new ArrayList<>();
        for (int i=0;i<segNum;i++){
            composeSourceList.add(ComposeSource.builder()
                    .bucket(minioConfig.getBucketName())
                    .object(md5.concat("-").concat(Integer.toString(i)))
                    .build());

        }
        try {
                minioClient.composeObject(ComposeObjectArgs.builder()
                    .bucket(minioConfig.getBucketName())
                    .object(fileName)
                    .sources(composeSourceList)
                    .build());
            log.info("minio: 文件切片合并成功");
            return true;
        } catch (Exception e) {
            log.error("minio: 合并文件失败",e);
            return false;
        }
    }

    /**
     * 删除分片文件
     * @param md5
     * @param segNum
     * @return
     */
    public Boolean removeTemp(String md5,Integer segNum){
        try {
            for(int i=0;i<segNum;i++) {
                minioClient.removeObject(RemoveObjectArgs.builder()
                        .bucket(minioConfig.getBucketName())
                        .object(md5.concat("-").concat(Integer.toString(i)))
                        .build());
            }
        } catch (Exception e) {
                log.error("minio: 移除临时文件失败");
                return false;
        }
        return true;
    }
    /**
     * 下载文件
     * @param fileName
     * @return
     */
    public ResponseEntity<byte[]> download(String fileName) {
        ResponseEntity<byte[]> responseEntity = null;
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = minioClient.getObject(GetObjectArgs.builder().bucket(minioConfig.getBucketName()).object(fileName).build());
            out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            //封装返回值
            byte[] bytes = out.toByteArray();
            HttpHeaders headers = new HttpHeaders();
            try {
                headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setAccessControlExposeHeaders(Arrays.asList("*"));
            responseEntity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.SUCCESS);
        } catch (Exception e) {
            log.error("minio: 下载文件失败",e);
        } finally {
            try {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseEntity;
    }

}
